#include <tenacitas.tuple_unpacker/unpack.h>
#include <iostream>
#include <cstdint>
#include <tuple>
#include <functional>



namespace tenacitas {
    namespace tuple_unpacker {
        class t00 {

        public:
            static void run( ){

                std::cout << "\n          t00" << std::endl;                
                test0( );
                // test1( );
                // test2( );
                // test3( );
                test4( );
                std::cout << "\n" << std::endl;
            }

        private:
            class a {
            public:

                a( int16_t p_i = 998 )
                    : m_i( p_i ) {
                    std::cout << "default constructor"
                              << ", m_i = " << m_i
                              << std::endl;
                }
                

                a( const a & p_a )
                : m_i ( p_a.m_i ) {
                    std::cout << "copy constructor" 
                              << ", m_i = " << m_i
                              << std::endl;
                }

                a( a && p_a )
                : a ( std::move( p_a.m_i ) ) {
                    p_a.m_i = -112;
                    std::cout << "move constructor"
                              << ", m_i = " << m_i
                              << std::endl;
                }
                                
                
                void m0( int16_t p_i ) {
                    std::cout << "a:m0: p_i = " << p_i
                              << ", m_i = " << m_i
                              << std::endl;
                }

                void m1( int16_t & p_i ) {
                    std::cout << "a:m_i p_i = " << p_i 
                              << ", m_i = " << m_i
                              << std::endl;
                }

                void m2( const int16_t & p_i ) {
                    std::cout << "a:m2: p_i = " << p_i 
                              << ", m_i = " << m_i
                              << std::endl;
                }

                void m3( int16_t && p_i ) {
                    std::cout << "a:m3: p_i = " << p_i 
                              << ", m_i = " << m_i
                              << std::endl;
                }

                int32_t m4( int16_t && p_i ) {
                    std::cout << "a:m3: p_i = " << p_i 
                              << ", m_i = " << m_i
                              << std::endl;
                    return ( 4 * p_i );
                }

                a & operator = ( const a & p_a ) {
                    m_i = p_a.m_i;
                    
                    std::cout << "copy assignment" 
                              << ", m_i = " << m_i
                              << std::endl;
                    return *this;
                }

                a & operator = ( a && p_a ) {
                    m_i = std::move( p_a.m_i );
                    p_a.m_i = -223;
                    std::cout << "move assignment" 
                              << ", m_i = " << m_i
                              << std::endl;
                    return *this;
                }

            private:
                int16_t m_i;
                
            };
            
            

            static void test0( ) {
                std::cout << "###### test0" << std::endl;

                typedef std::tuple<int16_t> tuple_type;                
                typedef unpack<void, int16_t> unpack_type;

                std::cout << "creating tuple" << std::endl;
                tuple_type l_tuple( -43 );

                std::cout << "creating a" << std::endl;
                a l_a;

                // std::cout << "biding" << std::endl;
                // function_type l_function = std::bind( &a::m0, std::ref( l_a ),
                //                                       std::placeholders::_1 ); 
                    
                unpack_type l_unpack;
                    
                l_unpack( std::move( l_tuple ), &a::m0, l_a );
            }

            static void test4( ) {
                std::cout << "###### test4" << std::endl;

                typedef std::tuple<int16_t> tuple_type;                
                typedef unpack<int32_t, int16_t> unpack_type;

                std::cout << "creating tuple" << std::endl;
                tuple_type l_tuple( -43 );

                std::cout << "creating a" << std::endl;
                a l_a;

                // std::cout << "biding" << std::endl;
                // function_type l_function = std::bind( &a::m0, std::ref( l_a ),
                //                                       std::placeholders::_1 ); 
                    
                unpack_type l_unpack;
                    
                l_unpack( std::move( l_tuple ), &a::m4, l_a );
            }

            // static void test1( ) {
            //     std::cout << "###### test1" << std::endl;
            //     typedef std::tuple<int16_t &> tuple_type;
            //     typedef std::function<void( int16_t & )> function_type;
            //     typedef unpack<tuple_type, function_type> unpack_type;

            //     int16_t l_i = 113;

            //     std::cout << "creating tuple" << std::endl;
            //     tuple_type l_tuple( l_i );

            //     std::cout << "creating a" << std::endl;
            //     a l_a;

            //     std::cout << "biding" << std::endl;
            //     function_type l_function = std::bind( &a::m1, std::ref( l_a ),
            //                                           std::placeholders::_1 ); 
                    
            //     unpack_type l_unpack;
                    
            //     l_unpack( std::move( l_tuple ), std::move( l_function ) );
            // }

            // static void test2( ) {
            //     std::cout << "###### test2" << std::endl;
            //     typedef std::tuple<const int16_t &> tuple_type;
            //     typedef std::function<void( const int16_t & )> function_type;
            //     typedef unpack<tuple_type, function_type> unpack_type;

            //     const int16_t l_i = 29;

            //     std::cout << "creating tuple" << std::endl;
            //     tuple_type l_tuple( l_i );

            //     std::cout << "creating a" << std::endl;
            //     a l_a;

            //     std::cout << "biding" << std::endl;
            //     function_type l_function = std::bind( &a::m2, std::ref( l_a ),
            //                                           std::placeholders::_1 ); 
                    
            //     unpack_type l_unpack;
                    
            //     l_unpack( std::move( l_tuple ), std::move( l_function ) );
            // }

            // static void test3( ) {
            //     std::cout << "###### test3" << std::endl;
                
            //     typedef std::tuple<int16_t &&> tuple_type;
            //     typedef std::function<void( int16_t && )> function_type;
            //     typedef unpack<tuple_type, function_type> unpack_type;

            //     int16_t l_i = -345;

            //     std::cout << "creating tuple" << std::endl;
            //     tuple_type l_tuple( std::move(l_i) );

            //     std::cout << "creating a" << std::endl;
            //     a l_a;

            //     std::cout << "biding" << std::endl;
            //     function_type l_function = std::bind( &a::m3, std::ref( l_a ),
            //                                           std::placeholders::_1 ); 
                    
            //     unpack_type l_unpack;
                    
            //     l_unpack( std::move( l_tuple ), std::move( l_function ) );
            // }


                
        };
    }
}

